/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月31日
 */
package com.massyframework.beanskin.license;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.util.version.VersionRange;

/**
 * 许可
 * @author huangkh
 */
public final class License implements Comparable<License>{
	
	private String groupId;
	private String moduleId;
	private VersionRange versionRange;
	
	private long customerId;
	private LocalDate expireDate;
	private Map<String, String> params;
	
	private String signature;
	private String x509Cert;
	private Integer providedId;
	
	/**
	 * 构造方法
	 * @param builder {@link Builder}
	 */
	protected License(Builder builder) {
		this.groupId = Objects.requireNonNull(builder.groupId, "\"groupId\" cannot be null.");
		this.moduleId = Objects.requireNonNull(builder.moduleId, "\"moduleId\" cannot be null.");
		this.versionRange = Objects.requireNonNull(builder.versionRange, "\"versionRange\" cannot be null.");
		
		this.customerId = Objects.requireNonNull(builder.customerId, "\"customerId\" cannot be null.").longValue();
		this.expireDate = builder.expireDate;
		
		this.params = builder.params == null ?
				Collections.emptyMap() :
					Collections.unmodifiableMap(builder.params);
		this.signature = Objects.requireNonNull(builder.signature, "\"signature\" cannot be null.");	
		this.x509Cert = Objects.requireNonNull(builder.x509cert, "\"x509cert\" cannot be null.");
		this.providedId = builder.providedId;
	}

	/**
	 * 组编号
	 * @return {@link String}
	 */
	public String getGroupId() {
		return this.groupId;
	}
	
	/**
	 * 模块编号
	 * @return {@link String}
	 */
	public String getModuleId() {
		return this.moduleId;
	}
	
	/**
	 * 版本区间
	 * @return {@link VersionRange}
	 */
	public VersionRange getVersionRange() {
		return this.versionRange;
	}
	
	/**
	 * 客户编号
	 * @return {@link long}
	 */
	public long getCustomerId() {
		return this.customerId;
	}
	
	/**
	 * 许可截止日
	 * <p>
	 * 返回null表示许可永久有效,否则为具体日期
	 * @return {@link Long}
	 */
	public LocalDate getExpireDate() {
		return this.expireDate;
	}
	
	/**
	 * 按{@code name}获取对应参数
	 * @param name {@link String},参数名
	 * @return {@link String},参数值,可能返回null.
	 */
	public String getParameter(String name) {
		return this.params.get(name);
	}
	
	/**
	 * 按{@code name}获取对应的参数
	 * @param name {@link String},参数名
	 * @param defaultValue {@link String},缺省值，当参数不存在，返回的值
	 * @return {@link String}
	 */
	public String getParameter(String name, String defaultValue) {
		String result = this.getParameter(name);
		return result == null ? defaultValue: result;
	}
	
	/**
	 * 所有许可参数
	 * @return {@link List}
	 */
	public List<String> getParameterNames(){
		return new ArrayList<>(this.params.keySet());
	}
	
	/**
	 * 签名
	 * @return {@link String},可能为null.
	 */
	public String getSignature() {
		return this.signature;
	}
	
	/**
	 * x509证书
	 * @return {@link String}
	 */
	public String getX509Cert() {
		return this.x509Cert;
	}
		
	/**
	 * {@code module}是否为许可提供模块
	 * <p>
	 * 当{@code module}被卸载时，需要进行比对,如果是，则需要撤销该许可。
	 * @param module {@link Module}
	 * @return {@link boolean},返回{@code true}表示是，否则返回{@code false}
	 */
	public boolean isProvidedModule(Module<?> module) {
		if (module == null) return false;
		if (this.providedId == null) return false;
		
		return module.getId() == this.providedId.intValue();
	}
	
	/**
	 * 签名的上下文内容
	 * @return {@link String}
	 */
	public String toContent() {
		StringBuilder builder = new StringBuilder();
		builder.append("module=")
			.append(this.getGroupId())
			.append(":").append(this.getModuleId())
			.append(":").append(this.getVersionRange().toString())
			.append(";");
		builder.append("customerId=")
			.append(Long.toString(this.getCustomerId()))
			.append(";");
		
		if (this.getExpireDate() != null) {
			builder.append("expiredDate=")
				.append(this.getExpireDate().toString())
				.append(";");
		}
		
		List<String> names = this.getParameterNames();
		if (!names.isEmpty()) {
			builder.append("[");
			Collections.sort(names);
			int size = names.size();
			for (int i=0; i<size; i++) {
				String name = names.get(i);
				String value = this.getParameter(name);
				builder.append(name).append("=").append(value);
				if (i != size -1) {
					builder.append(";");
				}
			}
			builder.append("]");
		}
		
		return builder.toString();
	}
		
	@Override
	public int compareTo(License o) {
		return this.expireDate.compareTo(o.getExpireDate());
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((signature == null) ? 0 : signature.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		License other = (License) obj;
		if (signature == null) {
			if (other.signature != null)
				return false;
		} else if (!signature.equals(other.signature))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "License [groupId=" + groupId + ", moduleId=" + moduleId + ", versionRange=" + versionRange
				+ ", customerId=" + customerId + ", expireDate=" + expireDate + "]";
	}
		
	/**
	 * 创建构建器实例
	 * @return {@link Builder}
	 */
	public static Builder newBuilder() {
		return new Builder();
	}
	
	/**
	 * 根据{@code license}创建构建器新实例
	 * @param license {@link License}
	 * @return {@link Builder}
	 */
	public static Builder newBuilder(License license) {
		if (license == null) {
			return newBuilder();
		}
		
		Builder result = new Builder()
				.groupId(license.groupId)
				.moduleId(license.moduleId)
				.versionRange(license.versionRange)
				.customerId(license.customerId)
				.expireDate(license.expireDate)
				.signature(license.signature)
				.x509cert(license.x509Cert);
		List<String> names = license.getParameterNames();
		for (String name: names) {
			result.addParameter(name, license.getParameter(name));
		}
		return result;
	}
	
	
	/**
	 * 构建器
	 * @author huangkh
	 *
	 */
	public static class Builder {
		protected String groupId;
		protected String moduleId;
		protected VersionRange versionRange;
		
		protected Long customerId;
		protected LocalDate expireDate;
		
		protected Map<String, String> params;
		protected String signature;
		protected String x509cert;
		protected Integer providedId;
		
		/**
		 * 设置组编号
		 * @param value {@link String}
		 * @return {@link Builder}
		 */
		public Builder groupId(String value) {
			this.groupId = value;
			return this;
		}
		
		/**
		 * 设置模块编号
		 * @param value {@link String}
		 * @return {@link Builder}
		 */
		public Builder moduleId(String value) {
			this.moduleId = value;
			return this;
		}
		
		/**
		 * 设置版本区间
		 * @param versionRange {@link VersionRange}
		 * @return {@link Builder}
		 */
		public Builder versionRange(String value) {
			if (value != null) {
				this.versionRange = VersionRange.valueOf(value);
			}
			return this;
		}
		
		/**
		 * 设置版本区间
		 * @param value {@link VersionRange}
		 * @return {@link Builder}
		 */
		public Builder versionRange(VersionRange value) {
			this.versionRange = value;
			return this;
		}
		
		/**
		 * 设置客户编号
		 * @param value {@link Long}
		 * @return {@link Builder}
		 */
		public Builder customerId(Long value) {
			this.customerId = value;
			return this;
		}
		
		/**
		 * 设置过期时间戳
		 * @param value {@link LocalDate}
		 * @return {@link Builder}
		 */
		public Builder expireDate(LocalDate value) {
			this.expireDate = value;
			return this;
		}
		
		/**
		 * 添加参数
		 * @param name {@link String},参数名
		 * @param value {@link String},参数值
		 * @return {@link Builder}
		 */
		public Builder addParameter(String name, String value) {
			if (name != null && value != null) {
				if (this.params == null) {
					this.params = new HashMap<>();
				}
				this.params.put(name.trim(), value.trim());
			}
			return this;
		}
		
		/**
		 * 设置签名
		 * @param value {@link String}
		 * @return {@link Builder}
		 */
		public Builder signature(String value) {
			this.signature = value;
			return this;
		}
		
		/**
		 * 设置x509证书
		 * @param value {@link String}
		 * @return {@link Builder}
		 */
		public Builder x509cert(String value) {
			this.x509cert = value;
			return this;
		}
		
		/**
		 * 设置提供证书的模块编号
		 * @param value {@link Integer}
		 * @return {@link Builder}
		 */
		public Builder providedModuleId(Integer value) {
			this.providedId = value;
			return this;
		}
		
		public String toContent() {
			StringBuilder builder = new StringBuilder();
			builder.append("module=")
				.append(this.groupId)
				.append(":").append(this.moduleId)
				.append(":").append(this.versionRange.toString())
				.append(";");
			builder.append("customerId=")
				.append(Long.toString(this.customerId))
				.append(";");
			
			if (this.expireDate != null) {
				builder.append("expiredDate=")
					.append(this.expireDate.toString())
					.append(";");
			}
			
			if (this.params != null) {
				List<String> names = new ArrayList<>(this.params.keySet());
				if (!names.isEmpty()) {
					builder.append("[");
					Collections.sort(names);
					int size = names.size();
					for (int i=0; i<size; i++) {
						String name = names.get(i);
						String value = this.params.get(name);
						builder.append(name).append("=").append(value);
						if (i != size -1) {
							builder.append(";");
						}
					}
					builder.append("]");
				}
			}
			
			return builder.toString();
		}
		
		/**
		 * 构建许可
		 * @return {@link License}
		 */
		public License build() {
			return new License(this);
		}
	}
}
